package com.lx.idgenerator.adaper;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.lx.idgenerator.constants.Constants;
import com.lx.idgenerator.exception.BizException;
import com.lx.idgenerator.model.LeafNode;
import com.lx.idgenerator.service.ILeafNodeService;
import com.lx.idgenerator.segement.leaf.ILeafInfoService;
import com.lx.idgenerator.segement.leaf.LeafInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Mysql Leaf适配
 *
 * @author wcy
 */
public class MysqlLeafInfoService implements ILeafInfoService {

    private static final int MAX_TRIES = 3;

    private static final Logger logger = LoggerFactory.getLogger(MysqlLeafInfoService.class);
    private ILeafNodeService leafNodeService;

    public MysqlLeafInfoService() {
    }

    public MysqlLeafInfoService(ILeafNodeService leafNodeService) {
        this.leafNodeService = leafNodeService;
    }

    @Override
    public LeafInfo getLeafInfo(String tag) {
        String[] split = splitTag(tag);
        String sysId = split[0];
        String bizTag = split[1];
        LeafNode leafNode = findLeafNode(sysId, bizTag);
        validateLeafNode(leafNode);
        return updateLeafNodeAndCreateLeafInfo(leafNode);
    }



    private String[] splitTag(String tag) {
        String[] split = tag.split(Constants.SPLIT_CHAR);
        if (split.length != 2) {
            throw new BizException("无效的tag格式:{" + tag + "}");
        }
        return split;
    }

    private LeafNode findLeafNode(String sysId, String bizTag) {
        LeafNode leafNode = leafNodeService.findBySystemIDAndBizTag(sysId, bizTag);
        if (leafNode == null) {
            throw new BizException("不存在此tag:{" + sysId + "|" + bizTag + "}的数据");
        }
        return leafNode;
    }

    private void validateLeafNode(LeafNode leafNode) {
        Integer step = leafNode.getStep();
        if (step <= 0) {
            throw new BizException("步长必须大于0");
        }
        Long maxId = leafNode.getMaxId();
        if (maxId < 1) {
            throw new BizException("最大id不能小于0");
        }
    }

    /**
     * 乐观锁保证多实例并发更新
     * @param leafNode
     * @return
     */
    private LeafInfo updateLeafNodeAndCreateLeafInfo(LeafNode leafNode) {
        //旧的最大id
        Long maxId = leafNode.getMaxId();
        Long newMaxId = calculateNewMaxId(leafNode, maxId);
        leafNode.setMaxId(newMaxId);
        leafNode.setUpdateTime(System.currentTimeMillis());
        LambdaUpdateWrapper<LeafNode> eq = new UpdateWrapper<LeafNode>().lambda()
                .eq(LeafNode::getSystemId, leafNode.getSystemId())
                .eq(LeafNode::getBizTag, leafNode.getBizTag())
                .eq(LeafNode::getMaxId, maxId);
        int i = 0;
        boolean update = false;
        while (i++ < MAX_TRIES && !update) {
            try {
                update = leafNodeService.update(leafNode, eq);
                if (!update) {
                    // 重新查询最新的数据
                    leafNode = findLeafNode(leafNode.getSystemId(), leafNode.getBizTag());
                    maxId = leafNode.getMaxId();
                    newMaxId = calculateNewMaxId(leafNode, maxId);
                    leafNode.setMaxId(newMaxId);
                    leafNode.setUpdateTime(System.currentTimeMillis());
                    eq = new UpdateWrapper<LeafNode>().lambda()
                            .eq(LeafNode::getSystemId, leafNode.getBizTag())
                            .eq(LeafNode::getBizTag, leafNode.getBizTag())
                            .eq(LeafNode::getMaxId, maxId);
                }
            } catch (BizException e) {
                // 记录其他异常
                logger.error("更新数据库时发生异常: " + e.getMessage(), e);
                throw new BizException("更新数据库失败");
            }
        }

        if (!update) {
            throw new BizException("更新数据库失败");
        }
        //返回
        LeafInfo leafInfo = new LeafInfo();
        leafInfo.setTag(leafNode.getBizTag());
        leafInfo.setCurId(maxId);
        leafInfo.setMaxId(newMaxId);
        leafInfo.setUpdateTime(leafNode.getUpdateTime());
        return leafInfo;
    }

    private Long calculateNewMaxId(LeafNode leafNode, Long maxId) {
        Integer step = leafNode.getStep();
        Long newMaxId = step + maxId;
        if (newMaxId < 1) {
            throw new BizException("新的最大id不能小于1");
        }
        return newMaxId;
    }

}
